# Dynamic Assets

:::info

This does not work with vite v5.0.0+ and wails v2 due to changes in vite. Changes are planned in v3 to support similar functionality under vite v5.0.0+. If you need this feature, stay with vite v4.0.0+. See [issue 3240](https://github.com/wailsapp/wails/issues/3240) for details

:::

If you want to load or generate assets for your frontend dynamically, you can achieve that using the [AssetsHandler](../reference/options#assetshandler) option. The AssetsHandler is a generic `http.Handler` which will be called for any non GET request on the assets server and for GET requests which can not be served from the bundled assets because the file is not found.

By installing a custom AssetsHandler, you can serve your own assets using a custom asset server.

## Example

In our example project, we will create a simple assets handler which will load files off disk:

```go title=main.go {17-36,49}
package main

import (
    "embed"
    "fmt"
    "github.com/wailsapp/wails/v2"
    "github.com/wailsapp/wails/v2/pkg/options"
    "github.com/wailsapp/wails/v2/pkg/options/assetserver"
    "net/http"
    "os"
    "strings"
)

//go:embed all:frontend/dist
var assets embed.FS

type FileLoader struct {
    http.Handler
}

func NewFileLoader() *FileLoader {
    return &FileLoader{}
}

func (h *FileLoader) ServeHTTP(res http.ResponseWriter, req *http.Request) {
    var err error
    requestedFilename := strings.TrimPrefix(req.URL.Path, "/")
    println("Requesting file:", requestedFilename)
    fileData, err := os.ReadFile(requestedFilename)
    if err != nil {
        res.WriteHeader(http.StatusBadRequest)
        res.Write([]byte(fmt.Sprintf("Could not load file %s", requestedFilename)))
    }

    res.Write(fileData)
}

func main() {
    // Create an instance of the app structure
    app := NewApp()

    // Create application with options
    err := wails.Run(&options.App{
        Title:  "helloworld",
        Width:  1024,
        Height: 768,
        AssetServer: &assetserver.Options{
            Assets:  assets,
            Handler: NewFileLoader(),
        },
        BackgroundColour: &options.RGBA{R: 27, G: 38, B: 54, A: 255},
        OnStartup:        app.startup,
        Bind: []interface{}{
            app,
        },
    })

    if err != nil {
        println("Error:", err)
    }
}
```

When we run the application in dev mode using `wails dev`, we will see the following output:

```
DEB | [ExternalAssetHandler] Loading 'http://localhost:3001/favicon.ico'
DEB | [ExternalAssetHandler] Loading 'http://localhost:3001/favicon.ico' failed, using AssetHandler
Requesting file: favicon.ico
```

As you can see, the assets handler is called when the default assets server is unable to serve the `favicon.ico` file.

If you right click the main application and select "inspect" to bring up the devtools, you can test this feature out by typing the following into the console:

```
let response = await fetch('does-not-exist.txt');
```

This will generate an error in the devtools. We can see that the error is what we expect, returned by our custom assets handler:

```mdx-code-block
<p className="text--center">
  <img
    src={require("@site/static/img/assetshandler-does-not-exist.webp").default}
  />
</p>
```

However, if we request `go.mod`, we will see the following output:

```mdx-code-block
<p className="text--center">
  <img src={require("@site/static/img/assetshandler-go-mod.webp").default} />
</p>
```

This technique can be used to load images directly into the page. If we updated our default vanilla template and replaced the logo image:

```html
<img id="logo" class="logo" />
```

with:

```html
<img src="build/appicon.png" style="width: 300px" />
```

Then we would see the following:

```mdx-code-block
<p className="text--center">
  <img
    src={require("@site/static/img/assetshandler-image.webp").default}
    style={{ width: "75%" }}
  />
</p>
```

:::warning

Exposing your filesystem in this way is a security risk. It is recommended that you properly manage access to your filesystem.

:::
